*! version 0.14 7jun2010 - byvar.ado included as subroutine; changed to version 10; dropped separate() option and restored sep() abbreviation; incorporated listsepformat() into format(); added abbreviate(). For SJ resubmission.
* version 0.13.1 26may2010 - in wide format, unlabelled variables are listed by their name, not numbered; seprefix abbreviation lengthened to sepr() to avoid confusion with sep() - but what does the latter do?
* version 0.13 8mar2010 - new listsep and listsepformat(bfmt pctfmt nfmt) options gives narrower & better formatted output; missing b <=> missing se; zero se changed to missing; better listing of problem observations
* version 0.12 4mar2010 - much clearer error message if -byvar- not installed
* version 0.11 23dec2009 - robust option; temporary byvar dropped from saving() file; keep before reshape means extra variables don't cause crash; ref(label) for long format; dflist() dropped; long() renamed methodvar(); only keeps variables that are needed (may leave problems if it crashes with clear option)
* version 0.10 7sep2009 - df() updated to df(var) dflist(varlist) dfprefix(string) dfsuffix(string)
* version 0.9 4sep2009 - renamed selist() as se()
* version 0.8 12feb2009 - corrected error in resetting big values to missing; new nomemcheck option
* version 0.7 8dec2009 - NEW SYNTAX simsum betalist, selist() [long(methodvar)]
* version 0.6 13nov2008 - new options firstmethod() and refmethod() 
* version 0.5 13jun2008 - works without by(); renamed simsum (was simoutwide)
* version 0.4  6jun2008 - modelse(mean) computes mean rather than RMSE; modelse_mcse corrected
* version 0.3 18feb2008 - clearer listing of funny obs; df() option
* version 0.2 19nov2007 - wide option, outsheet option, mcse option, if & in
prog def simsum
version 10

/*
To do list:
    improve output format?
        (a) if m=1, output statistics as columns?
        (b) if no by-variable, output statistics as rows?
        (c) single table, so that column widths match
*/

syntax varlist [if] [in], ///
    [true(string) METHodvar(varname) id(varlist)                             /// main options
    SEPrefix(string) SESuffix(string) se(varlist)                            /// SE options
    graph noMEMcheck max(real 10) semax(real 100) dropbig nolistbig listmiss /// data checking options
    level(real $S_level) by(varlist) mcse robust ref(string)                 /// calculation options
    df(string) DFPrefix(string) DFSuffix(string) MODELSEMethod(string)       /// calculation options
    bsims sesims bias empse relprec modelse relerror cover power             /// statistic options
    sepby(varlist) clear saving(string) ABbreviate(passthru)                 /// output options
    nolist listsep format(string) gen(string)                                /// output options
                                                                             /// undocumented options
    ]

// CHECK OPTIONS 

if "`modelsemethod'"=="" local modelsemethod rmse
if "`modelsemethod'"!="rmse" & "`modelsemethod'"!="mean" {
    di as error "Syntax: modelsemethod(rmse|mean)"
    exit 498
}

* SORT OUT BY
if "`by'"!="" {
    local byby by(`by')
    local byvar `by'
}
else {
    tempvar byvar
    gen `byvar'=0
}

* COUNT AND STORE BETA'S 
local i 0
foreach beta of varlist `varlist' {
    local ++i
    local beta`i' `beta'
    local betalist `betalist' `beta'
}
local m `i'

* SORT OUT SE'S
if "`seprefix'"!="" | "`sesuffix'"!="" {
    if "`se'"!="" {
        di as error "Can't specify se() with seprefix() or sesuffix()"
        exit 498
    }
    forvalues i=1/`m' {
        local se`i' `seprefix'`beta`i''`sesuffix'
        confirm var `se`i''
        local selist `selist' `se`i''
    }
}
else if "`se'"!="" {
    local i 0
    foreach sevar of varlist `se' {
        local ++i
        local se`i' `sevar'
        local selist `selist' `se`i''
    }
    if `i'<`m' {
        di as error "Fewer variables in se(`se') than in `betalist'"
        exit 498
    }
    if `i'>`m' {
        di as error "More variables in se(`se') than in `betalist'"
        exit 498
    }
}
else { // just working with beta's
    if "`sesims'`relprec'`modelse'`relerror'`cover'`power'"!="" {
        di as error "Can't compute `sesims' `relprec' `modelse' `relerror' `cover' `power' without standard errors"
        exit 498
    }
}   

* SORT OUT DF'S
if "`dfprefix'"!="" | "`dfsuffix'"!="" {
    if "`df'"!="" {
        di as error "Can't specify df() with dfprefix() or dfsuffix()"
        exit 498
    }
    forvalues i=1/`m' {
        local df`i' `dfprefix'`beta`i''`dfsuffix'
        confirm var `df`i''
        local dflist `dflist' `df`i''
    }
}
else if "`df'"!="" {
    cap confirm number `df'
    if !_rc local dftype number
    else {
        cap assert `df'==`df'
        if !_rc local dftype varname
        else {
            cap confirm var `df'
            if !_rc local dftype varlist
            else local dftype error
        }
    }
    if inlist("`dftype'","number","varname") {
        forvalues i=1/`m' {
            local df`i' `df'
        }
        if "`dftype'"=="varname" local dflist `df'
    }
    else if "`dftype'"=="varlist" {
        local i 0
        foreach dfvar of varlist `df' {
            local ++i
            local df`i' `dfvar'
            local dflist `dflist' `dfvar'
        }
        if `i'!=`m' local dftype error
    }
    if "`dftype'"=="error" {
        di as error "df must contain number, string or varlist of same length as estimates"
        exit 498
    }

}

* IF NO STATISTICS SPECIFIED, USE ALL AVAILABLE 
if "`bsims'`sesims'`bias'`empse'`relprec'`modelse'`relerror'`cover'`power'"=="" {
    foreach stat in bsims bias empse relprec {
        local `stat' `stat'
    }
    if "`se1'"!="" {
        foreach stat in sesims modelse relerror cover power {
            local `stat' `stat'
        }
    }
    if "`true'"=="" {
        di as text "true() not specified: can't calculate bias and coverage"
        local bias
        local cover
    }
}
local output `bsims' `sesims' `bias' `empse' `relprec' `modelse' `relerror' `cover' `power'
if "`bias'`empse'`relprec'`modelse'`relerror'`cover'`power'"=="" & "`mcse'"=="mcse" {
    di as error "Only bsims and/or sesims specified - mcse ignored"
    local mcse
}

if "`methodvar'"!="" {
    if `m'>1 {
        di as error "Only one estimate variable allowed with long format"
        exit 498
    }
    if "`id'"=="" {
        di as error "id() is required with long format"
        exit 498
    }
}

if "`list'"=="nolist" & "`clear'"=="" & "`saving'"=="" {
    di as error "No output requested! Please specify clear or saving(), or don't specify nolist"
    exit 498
}

if "`gen'"=="" local gen stat
cap confirm new variable `gen'num
local rc1=_rc
cap confirm new variable `gen'code
if _rc | `rc1' {
    di as error as smcl "{p}Variable `gen'num and/or `gen'code already exists. This is probably because the current data are -simsum- output. If this is what you want, use the gen() option.{p_end}"
    exit 498
}   

if "`memcheck'"!="nomemcheck" {
    qui desc, short
    if r(width)/r(widthmax)>0.45 {
        di as error "simsum is memory-hungry and can fail slowly if memory is more than 50% occupied."
        di as error as smcl "Please increase the memory using {help memory:set memory}, or use the nomemcheck option."
        exit 498
    }
}

// SET UP
marksample touse, novarlist

* check true is specified if bias or cover chosen
if "`bias'"=="bias" | "`cover'"=="cover" {
    if "`true'"=="" {
        di as error "true() must be specified when bias and/or cover is requested"
        exit 498
    }
    tempvar truevar
    qui gen `truevar' = `true'
    qui count if missing(`truevar') & `touse'
    if r(N)>0 {
        di as error "Missing values found for true value `true'"
        exit 498
    }
}

// START CALCULATION
preserve
qui keep if `touse'

// CONVERT FROM LONG FORMAT IF NECESSARY, EXTRACT METHOD LABELS AND FIND REFERENCE METHOD
if "`methodvar'"!="" {
    * DATA ARE LONG, CONVERTING TO WIDE
    qui levelsof `methodvar', local(methods)
    local label : val label `methodvar'
    local i 0
    foreach method in `methods' {
        local ++i
        local beta`i' `betalist'`method'
        local se`i'   `selist'`method'
        local newbetalist `newbetalist' `betalist'`method'
        local newselist   `newselist'   `selist'`method'
        if "`dftype'"=="number" local df`i' `df'
        if "`dftype'"=="varname" local df`i' `dflist'`method'
        if "`label'"!="" local label`i' : label `label' `method'
        else local label`i' "`method'"
        if "`label`i''"=="`ref'" local refmethod `i'
    }
    local m `i'
    if "`refmethod'"=="" {
        if "`ref'"!="" {
            if "`label'"!="" local labelled "labelled "
            di as error "ref(`ref') is not one of the `labelled'values of `methodvar'"
            exit 498
        }
        else local refmethod 1
    }
    di as text "Reshaping data to wide format ..."
    keep `betalist' `selist' `dflist' `by' `byvar' `id' `methodvar' `touse' `truevar'
    cap confirm string var `methodvar'
    if _rc==0 local string string
    qui reshape wide `betalist' `selist' `dflist', i(`by' `id') j(`methodvar') `string'
    local betalist `newbetalist'
    local selist `newselist'
}
else { // DATA ARE ALREADY WIDE
    forvalues i=1/`m' {
        local label`i' : var label `beta`i''
        if "`label`i''"=="" local label`i' "`beta`i''" /* corrected, v0.13.1 */
        if "`beta`i''"=="`ref'" local refmethod `i'
    }
    if "`refmethod'"=="" {
        if "`ref'"!="" {
            di as error "ref(`ref') is not one of the listed point estimates"
            exit 498
        }
        else local refmethod 1
    }
    keep `betalist' `selist' `dflist' `by' `byvar' `id' `touse' `truevar' 
}

// LIST MISSING/PROBLEM OBS
tempvar missing
gen `missing' = 0
forvalues i=1/`m' {
    qui replace `missing' = missing(`beta`i'') & `touse'
    if "`se`i''"!="" qui replace `missing' = 1 if missing(`se`i'') & `touse'
    if "`se`i''"!="" qui replace `missing' = 1 if `se`i''==0 & `touse'
    qui count if `missing'
    if r(N)>0 {
        if "`se`i''"!="" {
            qui replace `missing' = missing(`beta`i'') & missing(`se`i'') & `touse'
            qui count if `missing'
            if r(N)>0 {
                di as text _new "Warning: found " as result r(N) as text " observations with both `beta`i'' and `se`i'' missing" _c
                if "`listmiss'"=="listmiss" list `by' `id' `beta`i'' `se`i'' if `missing', sepby(`sepby')
                di as text "--> no action taken"
            }

            qui replace `missing' = !missing(`se`i'') & missing(`beta`i'') & `touse'
            qui count if `missing'
            if r(N)>0 {
                di as text _new "Warning: found " as result r(N) as text " observed values of `se`i'' with missing `beta`i''" _c
                if "`listmiss'"=="listmiss" list `by' `id' `beta`i'' `se`i'' if `missing', sepby(`sepby')
                qui replace `se`i'' = . if `missing'
                di as text "--> `se`i'' changed to missing"
            }

            qui replace `missing' = missing(`se`i'') & !missing(`beta`i'') & `touse'
            qui count if `missing'
            if r(N)>0 {
                di as text _new "Warning: found " as result r(N) as text " observed values of `beta`i'' with missing `se`i''" _c
                if "`listmiss'"=="listmiss" list `by' `id' `beta`i'' `se`i'' if `missing', sepby(`sepby')
                qui replace `beta`i'' = . if `missing'
                di as text "--> `beta`i'' changed to missing"
            }

            qui replace `missing' = (`se`i''==0) & `touse'
            qui count if `missing'
            if r(N)>0 {
                di as text _new "Warning: found " as result r(N) as text " zero values of `se`i''" _c
                if "`listmiss'"=="listmiss" list `by' `id' `beta`i'' `se`i'' if `missing', sepby(`sepby')
                qui replace `beta`i'' = . if `missing'
                qui replace `se`i'' = . if `missing'
                di as text "--> `beta`i'' and `se`i'' have been changed to missing values for these observations"
            }
        }
    }
}
drop `missing'

// CHECK FOR TOO-BIG OBS & OPTIONALLY LIST / DROP THEM
tempvar infb infse
gen `infb' = 0
gen `infse' = 0
local errorbig 0
forvalues i=1/`m' {
    qui summ `beta`i''
    qui replace `infb' = (abs(`beta`i''-r(mean))/r(sd) > `max') & !missing(`beta`i'')  
    if "`se`i''"!="" {
        qui summ `se`i''
        qui replace `infse' = (`se`i''/r(mean) > `semax') & !missing(`se`i'') 
    }
    qui count if `infb'
    local ninfb = r(N)
    qui count if `infse'
    local ninfse = r(N)
    if `ninfb'+`ninfse' > 0 {
        di as text _newline `"Warning: found "' as result `ninfb' as text `" observations with standardised `beta`i'' > `max'"' _c
        if "`se`i''"!="" di as text `" and "' as result `ninfse' as text `" observations with scaled `se`i'' > `semax'"' _c
        if "`listbig'"!="nolistbig" l `by' `id' `beta`i'' `se`i'' if `infb'|`infse', sepby(`sepby')
        if "`dropbig'"=="dropbig" {
            qui replace `beta`i'' = . if `infb'|`infse'
            if "`se`i''"!="" qui replace `se`i'' = . if `infb'|`infse'
            di as text `"--> `beta`i'' "' _c
            if "`se`i''"!="" di as text `"and `se`i'' "' _c
            di as text `"have been changed to missing values for these observations"'
        }
        else local errorbig 1
    }
}
if `errorbig' {
    di as error "Use dropbig option to drop these observations"
    if "`listbig'"=="nolistbig" di as error "Remove nolistbig option to list these observations"
    di as error "Use max() option to change acceptable limit of point estimates"
    if "`se'"!="" di as error "Use semax() option to change acceptable limit of standard errors"
    exit 498
}

// OPTIONAL DESCRIPTIVE GRAPH
if "`graph'"=="graph" {
    tempfile graph
    set graphics off
    forvalues i=1/`m' {
        cap gr7 `se`i'' `beta`i'', xla yla b2title("`beta`i''") l1title("`se`i''") t1title("`label`i''") saving(`graph'`i', replace) /*`byby'*/
        if !_rc local gphlist `gphlist' `graph'`i'
    }
    set graphics on
    gr7 using `gphlist', title(Std error vs. point estimate by method)
}

// PROCESS RESULTS
di as text _newline "Starting to process results ..."
if `level'<1 local level=`level'*100
if "`robust'"=="robust" & ("`relprec'"=="relprec" | "`relprec'"=="relprec" | "`relerror'"=="relerror") {
    forvalues i=1/`m' {
        tempvar betamean`i'
        egen `betamean`i'' = mean(`beta`i''), `byby'        
    }
}
forvalues i=1/`m' {
    if "`df`i''"!="" local crit`i' invttail(`df`i'',(1-`level'/100)/2)
    else             local crit`i' = -invnorm((1-`level'/100)/2)
    if "`dftype'"=="number" local crit`i' = `crit`i'' // for speed
    local collcount `collcount' bsims_`i'=`beta`i''
    if "`bias'"=="bias" {
        qui gen bias_`i' = `beta`i'' - `truevar'
        local collmean `collmean' bias_`i' 
    }
    if "`relerror'"=="relerror" | "`modelse'"=="modelse" {
        qui gen var_`i'=`se`i''^2
    }
    if "`empse'"=="empse" | "`relerror'"=="relerror" | "`relprec'"=="relprec" | "`bias'"=="bias" {
        local collsd `collsd' empse_`i'=`beta`i''
    }
    if "`relprec'"=="relprec" & `i'!=`refmethod' {
        qui byvar `byvar', r(rho N) gen unique: corr `beta`refmethod'' `beta`i''
        rename Rrho_ corr_`i'
        rename RN_ ncorr_`i'
        local collsum `collsum' corr_`i' ncorr_`i'
    }
    if "`modelse'"=="modelse" | "`relerror'"=="relerror" | "`sesims'"=="sesims" {
        local collcount `collcount' sesims_`i'=`se`i'' 
    }
    if "`modelse'"=="modelse" | "`relerror'"=="relerror" {
        local collmean `collmean' modelse_`i'=`se`i''
        local collmean `collmean' varmean_`i'=var_`i'
        local collsd `collsd' varsd_`i'=var_`i' 
        local collsd `collsd' modelsesd_`i'=`se`i''
    }
    if "`cover'"=="cover" | "`power'"=="power" {
        if "`cover'"=="cover" local collcount `collcount' bothsims_`i'=cover_`i'
        else local collcount `collcount' bothsims_`i'=power_`i'
    }
    if "`cover'"=="cover" {
        qui gen cover_`i' = 100*(abs(`beta`i''-`truevar')<(`crit`i'')*`se`i'') if !missing(`beta`i'') &   !missing(`se`i'') 
        local collmean `collmean' cover_`i' 
    }
    if "`power'"=="power" {
        qui gen power_`i' = 100*(abs(`beta`i'')>=(`crit`i'')*`se`i'') if !missing(`beta`i'') & !missing(`se`i'') 
        local collmean `collmean' power_`i' 
    }
    if "`robust'"=="robust" {
        if "`empse'"=="empse" {
            tempvar empseT`i' empseB`i' empseTT`i' empseBB`i' empseTB`i' 
            qui gen `empseT`i'' = (`beta`i''-`betamean`i'')^2
            qui gen `empseB`i'' = 1
            qui gen `empseTT`i'' = `empseT`i''^2
            qui gen `empseBB`i'' = `empseB`i''^2
            qui gen `empseTB`i'' = `empseT`i''*`empseB`i''
            local collsum `collsum' `empseT`i'' `empseB`i'' `empseTT`i'' `empseTB`i'' `empseBB`i''
        }
        if "`relprec'"=="relprec" {
            tempvar relprecT`i' relprecB`i' relprecTT`i' relprecBB`i' relprecTB`i' 
            qui gen `relprecT`i'' = (`beta`refmethod''-`betamean`refmethod'')^2
            qui gen `relprecB`i'' = (`beta`i''-`betamean`i'')^2
            qui gen `relprecTT`i'' = `relprecT`i''^2
            qui gen `relprecBB`i'' = `relprecB`i''^2
            qui gen `relprecTB`i'' = `relprecT`i''*`relprecB`i''
            local collsum `collsum' `relprecT`i'' `relprecB`i'' `relprecTT`i'' `relprecTB`i'' `relprecBB`i''
        }
        if "`relerror'"=="relerror" {
            tempvar relerrorT`i' relerrorB`i' relerrorTT`i' relerrorBB`i' relerrorTB`i' 
            qui gen `relerrorT`i'' = `se`i''^2
            qui gen `relerrorB`i'' = (`beta`i''-`betamean`i'')^2
            qui gen `relerrorTT`i'' = `relerrorT`i''^2
            qui gen `relerrorBB`i'' = `relerrorB`i''^2
            qui gen `relerrorTB`i'' = `relerrorT`i''*`relerrorB`i''
            local collsum `collsum' `relerrorT`i'' `relerrorB`i'' `relerrorTT`i'' `relerrorTB`i'' `relerrorBB`i''
        }
    }
}
if "`collmean'"!="" local collmean (mean) `collmean'
if "`collsd'"!="" local collsd (sd) `collsd'
if "`collcount'"!="" local collcount (count) `collcount'
if "`collsum'"!="" local collsum (sum) `collsum'
collapse `collmean' `collsd' `collcount' `collsum', by(`byvar')
forvalues i=1/`m' {
    qui gen k_`i' = bsims_`i'/(bsims_`i'-1)
    if "`bias'"=="bias" {
        qui gen bias_mcse_`i' = empse_`i' / sqrt(bsims_`i')
    }
    if ("`empse'"=="empse"  | "`relerror'"=="relerror") & "`robust'"=="" {
        qui gen empse_mcse_`i' = empse_`i'/sqrt(2*(bsims_`i'-1))
    }
    else if ("`empse'"=="empse") & "`robust'"=="robust" {
        qui replace `empseTT`i''=`empseTT`i''*(k_`i'^2)
        qui replace `empseTB`i''=`empseTB`i''*k_`i'
        qui replace `empseT`i'' =`empseT`i'' *k_`i'
        qui gen empse_mcse_`i' = sqrt(k_`i') * sqrt(`empseTT`i'' -2*(`empseT`i''/`empseB`i'')*`empseTB`i'' +(`empseT`i''/`empseB`i'')^2*`empseBB`i'') / `empseB`i''
        qui replace empse_mcse_`i' = empse_mcse_`i' / (2*empse_`i')
    }
    if "`relprec'"=="relprec" {
        if `i'!=`refmethod' {
            qui gen relprec_`i' = 100 * ((empse_`refmethod'/empse_`i')^2-1)
            if "`robust'"=="" {
                qui gen relprec_mcse_`i' = 200 * (empse_`refmethod'/empse_`i')^2 * sqrt((1-(corr_`i')^2)/(ncorr_`i'-2))
            }
            else {
                qui gen relprec_mcse_`i' = 100 * sqrt(`relprecTT`i'' -2*(`relprecT`i''/`relprecB`i'')*`relprecTB`i'' +(`relprecT`i''/`relprecB`i'')^2*`relprecBB`i'') / `relprecB`i''
            }
        }
        else {
            qui gen relprec_`i' = .
            qui gen relprec_mcse_`i' = .
        }
    }
    if "`modelse'"=="modelse" | "`relerror'"=="relerror" {
        if "`modelsemethod'"=="rmse" {
            qui replace modelse_`i' = sqrt(varmean_`i')
            qui gen modelse_mcse_`i' = varsd_`i' / sqrt(4 * sesims_`i' * varmean_`i') 
        }
        else if "`modelsemethod'"=="mean" {
            qui gen modelse_mcse_`i' = modelsesd_`i' / sqrt(sesims_`i')
        }
    }
    if "`relerror'"=="relerror" {
        qui gen relerror_`i' = 100*(modelse_`i'/empse_`i'-1)
        if "`robust'"=="" qui gen relerror_mcse_`i' = 100*(modelse_`i'/empse_`i') * sqrt((modelse_mcse_`i'/modelse_`i')^2 + (empse_mcse_`i'/empse_`i')^2 )
        else {
            qui gen relerror_mcse_`i' = sqrt(`relerrorTT`i'' -2*(`relerrorT`i''/`relerrorB`i'')*`relerrorTB`i'' +(`relerrorT`i''/`relerrorB`i'')^2*`relerrorBB`i'') / `relerrorB`i''
            qui replace relerror_mcse_`i' = relerror_mcse_`i' * 100 / (2*(1+relerror_`i'/100))
        }
    }
    if "`cover'"=="cover" {
        qui gen cover_mcse_`i' = sqrt(cover_`i'*(100-cover_`i')/bothsims_`i')
    }
    if "`power'"=="power" {
        qui gen power_mcse_`i' = sqrt(power_`i'*(100-power_`i')/bothsims_`i') 
    }
    cap drop varmean_`i' 
    cap drop varsd_`i'
}

// PREPARE FOR OUTPUT
local alpha=100-`level'
local bsimsname Non-missing point estimates
local sesimsname Non-missing standard errors
local biasname Bias in point estimate
local empsename Empirical standard error
local relprecname % gain in precision relative to method `label`refmethod''
if "`modelsemethod'" =="mean" local modelsename Mean model-based standard error `sebeta'
if "`modelsemethod'" =="rmse" local modelsename RMS model-based standard error `sebeta'
local relerrorname Relative % error in standard error
local covername Coverage of nominal `level'% confidence interval
local powername Power of `alpha'% level test

local keeplist `byvar'
foreach name in `output' {
    local domcse = "`mcse'"=="mcse" & "`name'"!="bsims" & "`name'"!="sesims"
    forvalues i=1/`m' {
        rename `name'_`i' method`i'`name'
        local keeplist `keeplist' method`i'`name'
        if `domcse' {
            rename `name'_mcse_`i' method`i'`name'_mcse
            local keeplist `keeplist' method`i'`name'_mcse
        }
    }
}
forvalues i=1/`m' {
    local methodlist `methodlist' method`i'
}
keep `keeplist'
qui reshape long `methodlist', i(`byvar') j(`gen'code) string
forvalues i=1/`m' {
    char method`i'[varname] "`label`i''"
    label var method`i' "`label`i''"
}
local i 0
qui gen mcse = .
qui gen `gen'num = .
foreach stat in bsims sesims bias empse relprec modelse relerror cover power {
    local ++i
    qui replace mcse=0 if `gen'code=="`stat'"
    qui replace mcse=1 if `gen'code=="`stat'_mcse"
    qui replace `gen'code="`stat'" if `gen'code=="`stat'_mcse"
    qui replace `gen'num = `i' if `gen'code=="`stat'" | `gen'code=="`stat'_mcse"
}
* label statistics
label def `gen'num  1 "Non-missing point estimates", add
label def `gen'num  2 "Non-missing standard errors", add
label def `gen'num  3 "Bias in point estimate", add
label def `gen'num  4 "Empirical standard error", add
label def `gen'num  5 "% gain in precision relative to method `label`refmethod''", add
if "`modelsemethod'" =="mean" label def `gen'num  6 "Mean model-based standard error", add
if "`modelsemethod'" =="rmse" label def `gen'num  6 "RMS model-based standard error", add
label def `gen'num  7 "Relative % error in standard error", add
label def `gen'num  8 "Coverage of nominal `level'% confidence interval", add
label def `gen'num  9 "Power of `alpha'% level test", add
label val `gen'num `gen'num 
assert !mi(mcse)
foreach var in `methodlist' {
    rename `var' `var'_
    local methodlist2 `methodlist2' `var'_
}
qui reshape wide `methodlist2', i(`byvar' `gen'num) j(mcse)
local ids `gen'num `by'
local betas
forvalues i=1/`m' {
    rename method`i'_0 `beta`i''
    label var `beta`i'' "`label`i''"
    char `beta`i''[varname] "`label`i''"
    local betasnomcse `betasnomcse' `beta`i''
    local betas `betas' `beta`i''
    if "`mcse'"=="mcse" {
        rename method`i'_1 `beta`i''_mcse
        local betas `betas' `beta`i''_mcse
        label var `beta`i''_mcse "`label`i'' (MCse)"
        char `beta`i''_mcse[varname] "(MCse)"
    }
}
char `gen'num[varname] "Statistic"
label var `gen'num "Statistic"
label var `gen'code "Statistic code"
order `ids' `betas'
sort `gen'num `by'

// OUTPUT
tokenize `format'
local bfmt "`1'"
local pctfmt "`2'"
local nfmt "`3'"
if "`bfmt'"=="" local bfmt: format `beta1'
if "`pctfmt'"=="" | "`pctfmt'"=="=" | "`pctfmt'"=="." local pctfmt `bfmt'
if "`nfmt'"=="" local nfmt %7.0f
if "`list'"!="nolist" {
    if "`listsep'"=="" {
        qui format `betas' `bfmt' 
        list `ids' `betas', noo subvarname sepby(`gen'num `sepby') `abbreviate'
    }
    else {
        foreach stat in `output' {
            di as text _new "``stat'name'"
            local thisbetas = cond(inlist("`stat'","bsims","sesims"), "betasnomcse", "betas")
            if inlist("`stat'","bsims","sesims") local format `nfmt'
            else if inlist("`stat'","bias","empse","modelse") local format `bfmt'
            else local format `pctfmt'
            qui format `betas' `format' 
            list `by' ``thisbetas'' if `gen'code=="`stat'", noo subvarname sepby(`gen'num `sepby') `abbreviate'
        }
    }
}
* format for output data set
qui format `betas' `bfmt'

// FINISH OFF
char `gen'num[varname] 
if "`saving'"!="" {
    if "`by'"=="" drop `byvar'
    save `saving'
}
if "`clear'"=="clear" {
    restore, not
    di as text "Results are now in memory."
}
end

********************** BYVAR (PATRICK ROYSTON) ********************************

*! version 2.0.3 PR 03apr2007.
program define byvar, rclass sortpreserve
version 8

tokenize `"`0'"', parse(":")
if "`2'"!=":" {
    di as err "syntax error - maybe missing colon, or unmatched quotes somewhere"
    exit 198
}
local 0 `1'
mac shift 2 // skip the colon
local command `*'

syntax varlist [if] [in], [ E(str) R(str) B(str) SE(str) GRoup(str) GEnerate ///
 Tabulate REturn Missing Pause noLabel Unique SEParator(string) ]

if "`group'"!="" confirm new var `group'

if "`unique'"!="" & "`generate'"=="" {
    di as err "unique requires generate"
    exit 198
}

local bylist `varlist'

local s1 `e'
local L1 E
local s2 `r'
local L2 R
local s3 `b'
local L3 B
local s4 `se'
local L4 S
local tostore=`"`s1'`s2'`s3'`s4'"'!=""
if !`tostore' & "`generate'`return'`tabulate'"!="" {
    di as err "nothing to generate, return or tabulate"
    exit 198
}
if `tostore' & "`generate'`return'`tabulate'"=="" {
    di as txt "(tabulate assumed)"
    local tabulat tabulate
}

local tab="`tabulate'"!="" & `tostore'

* Extract multiple commands
local ncmd 0
if "`separator'"!="" local sep=substr("`separator'",1,1)
else local sep @
tokenize `"`command'"', parse("`sep'")
while `"`1'"'!="" {
    if `"`1'"'!="@" {
        local ++ncmd
        local command`ncmd' `1'
    }
    mac shift
}
forvalues i=1/`ncmd' {
    tokenize `"`command`i''"', parse(",")
    local cmd1 `1'
    mac shift
    local cmd2`i' `*'
    local pif=index(`"`cmd1'"', " if ")
    if `pif'>0 {
        local cmd11`i'=substr(`"`cmd1'"',1,`pif')
        local cmd12`i'=substr(`"`cmd1'"',`pif'+4,.)
        local ifalso`i' " & "
    }
    else local cmd11`i' `cmd1'
}

/* Number and count the groups  */
quietly {
/*
    Default is to exclude missing groups in bylist from analysis.
    Their corresponding group codes are set to missing and sorted to
    end of data.
*/
    tempvar grp first
    marksample touse, strok
    if "`missing'"=="" {
        markout `touse' `bylist', strok
        replace `touse'=. if `touse'==0
    }
    sort `touse' `bylist'
    by `touse' `bylist': gen byte `first'=1 if _n==1 & `touse'==1
    gen int `grp'=sum(`first') if `touse'==1
    drop `touse'
    sum `grp'
    local GRP=r(max)
    if `GRP'==0 noisily error 2000

    local itemlen 14
/*
    Extract group-defining values of bylist variables
    and store in macros
*/
    local nby : word count `bylist'
    if `tab' {
        noi di
        local dashes
    }
    tempvar index
    gen long `index'=.
    forvalues i=1/`GRP' {
        replace `index'=sum(_n*(`grp'==`i' & `first'==1))
        local j=`index'[_N]
        forvalues k=1/`nby' {
            local byvar`k' : word `k' of `bylist'
            local vallab: value label `byvar`k''
            local byval=`byvar`k''[`j']
            if "`vallab'"!="" & "`label'"!="nolabel" {
                local by`i'`k': label `vallab' `byval'
            }
            else local by`i'`k' `byval'
            if `i'==1 & `tab' {
                local dashes "`dashes'---------"
                noi di as txt %-9s "`byvar`k''" _c
            }
        }
    }
    drop `index'
}
if `tab' {
    di as txt " |" _c
    local dashes "`dashes'-+"
}
/*
    Parse and record items for storage
*/
forvalues j=1/4 {
    local i 1
    if `"`s`j''"'!="" {
        tokenize `"`s`j''"'
        * take care of embedded quotes
        local k 1
        while `"`1'"'!="" {
            local sk`k' `sk`k'' `1'
            * Count number of quotes in `1' - should be 2. Do not actually change `1' (discard `z').
            local z: subinstr local 1 `"""' "Q", all count(local nquote)    // "
            if `nquote'>0 & `nquote'!=2 {
                di as err "unmatched quotes in " `"`s`j''"'
                exit 198
            }
            local ++k
            mac shift
        }
        local l 1
        while `l'<`k' {
            * 1=item, [ 2="=", 3=description of item ], 4=null
            tokenize `"`sk`l''"', parse("=")
            local sk`l'
            if `"`4'"'!="" {
                di as err "invalid " `"`sk`l''"'
                exit 198
            }
            local st`j'`i' `1'
            if `"`3'"'=="" {
                if      `j'==1 local lab e(`1')
                else if `j'==2 local lab r(`1')
                else if `j'==3 local lab _b[`1']
                else if `j'==4 local lab _se[`1']
            }
            else {
                local lab=substr(`"`3'"',1,`itemlen'-1)
            }
            if "`generate'"!="" {
                mk_name `L`j'' `1' 6
                local `L`j''_`i' `s(name)'
                qui gen double ``L`j''_`i''=.
                lab var ``L`j''_`i'' `"`lab' by `bylist'"'
            }
            if `tab' {
                local dashes "`dashes'--------------"
                local skip=`itemlen'-length(`"`lab'"')
                di as txt _skip(`skip') `"`lab'"' _c
            }
            local ++i
            local ++l
        }
    }       
    local n`j'=`i'-1
}
if `tab' di _n as txt "`dashes'"

/* Perform calcs        */
if `tab' local show quietly
else local show noisily
tempname thing
forvalues i=1/`GRP' { // i indexes members of groups implied by bylist
    if !`tab' {
        di as txt _n "-> " _c
        forvalues k=1/`nby' {
            di as txt "`byvar`k''==`by`i'`k'' " _c
        }
        di
    }
    forvalues j=1/`ncmd' {
*di `"capture `show' `cmd11`j'' if `grp'==`i' `ifalso`j'' `cmd12`j'' `cmd2`j''"'
        capture `show' `cmd11`j'' if `grp'==`i' `ifalso`j'' `cmd12`j'' `cmd2`j''
        local rc=_rc
    }

    if "`pause'"!="" more

    if `tab' {
        forvalues k=1/`nby' {
            di as res %-9s substr("`by`i'`k''",1,8) _c
        }
        di as txt " |" _c
    }
    forvalues k=1/4 {   // k indexes the 4 types of thing to be stored
        if `n`k''>0 {
            forvalues l=1/`n`k'' { /* l indexes # of thing */
                
                if `rc'==0 {
                    if `k'==1   scalar `thing'=e(`st`k'`l'')
                    else if `k'==2  scalar `thing'=r(`st`k'`l'')
                    else if `k'==3  scalar `thing'=_b[`st`k'`l'']
                    else if `k'==4  scalar `thing'=_se[`st`k'`l'']
                }
                else scalar `thing'=.
                if "`generate'"!="" {
                    if "`unique'"=="" qui replace ``L`k''_`l''=`thing' if `grp'==`i'
                    else qui replace ``L`k''_`l''=`thing' if `grp'==`i' & `first'==1
                }
                if "`return'"!="" {
                    * gp refers to subgroup (level) of the byvar(s)
                    local r `L`k''`l'gp`i'
                    return scalar `r'=`thing'
                }
                if `tab' di _skip(4) as res %10.0g `thing' _c
            }
        }
    }
    if `tab' di
}
quietly if "`generate'"!="" {
    forvalues k=1/4 {           /* k indexes type of thing stored */
        if `n`k''>0 {
            forvalues i=1/`n`k'' { /* i indexes item in list of things */
                compress ``L`k''_`i''
                // if "`firstonly'"!="" bysort `grp': replace ``L`k''_`i''=. if _n>1
                return local `L`k''_`i' ``L`k''_`i''
            }
        }
    }
}
if "`group'"!="" {
    cap drop `group'
    rename `grp' `group'
    lab var `group' "group by `bylist'"
}
return scalar byvar_g=`GRP'
end

program define mk_name, sclass
/* meaning make_unique_name <letter> <suggested_name> <#_chars> */
    version 8
    args letter base numchar
    sret clear
    local name = substr("`letter'`base'",1,`numchar'+1)
    xi_mkun2 `name'_
end

program define xi_mkun2, sclass
version 8
/* meaning make_unique_name <suggested_name> */
    args name

    local totry "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    local lentot=length("`totry'")
    local l 0
    local len = length("`name'")
    capture list `name'* in 1       /* try name out */
    while _rc==0 {
        if `l'==`lentot' {
            di as err "too many terms---limit is " `lentot'+1
            exit 499
        }
        local l=`l'+1
        local name = substr("`name'",1,`len'-1)+substr("`totry'",`l',1)
        capture list `name'* in 1
    }
    sret local name "`name'"
end

********************************************************************************************************
